#
#  Compression analysis
#  'Legenf of Mana (USA)' - Play Station
#  (c) CUE 2009
#
#  IN:
#    *a0 = pak pointer
#    *a1 = raw pointer
#     a2 = pak_end pointer
#     a3 = raw_end pointer
#
#  OUT:
#     v0 = 0/ok, 1/fail
#    *a0 = pak_data_ptr if ok, pak_code_ptr if fail
#    *a1 = raw_end if ok, raw_ptr if fail
#
#  compressed file:
#    01 .................... magic number
#    [code + bytes] ........ chunk(s)
#    FF .................... final chunk (end of compressed data)
#
#  code list:
#    00-EF ................. STORE
#      N+{list} ............   put (N+1) times {byte}
#    F0-F4 ................. RLE repeat
#      F0+XN ...............   put (N+3) times {X}
#      F1+N+X ..............   put (N+4) times {X}
#      F2+N+YX .............   put (N+2) times {X,Y}
#      F3+N+X+Y ............   put (N+2) times {X,Y}
#      F4+N+X+Y+Z ..........   put (N+2) times {X,Y,Z}
#    F5-F7 ................. RLE list
#      F5+N+X+{list} .......   put (N+4) times {X,byte}
#      F6+N+X+Y+{list} .....   put (N+3) times {X,Y,byte}
#      F7+N+X+Y+Z+{list} ...   put (N+2) times {X,Y,Z,byte}
#    F8-FB ................. RLE increment
#      F8+N+X ..............   put from {X} to {X+(N+3)}
#      F9+N+X ..............   put from {X} to {X-(N+3)}
#      FA+N+X+I ............   put from {X} to {X+(N+4)*I)}
#      FB+N+X+Y+I ..........   put from {YX} to {YX+(N+2)*I}
#    FC-FE ................. LZSS
#      FC+XY+NZ.... ........   put (N+4) bytes from '$-(ZXY+1)'
#      FD+X+N ..............   put (N+20) bytes from '$-(X+1)'
#      FE+XN ...............   put (N+3) bytes from '$-8*(X+1)'
#    FF .................... end of compressed data
#
         .data
@table:  .word @codeF0                  # table of code-X offsets
         .word @codeF1
         .word @codeF2
         .word @codeF3
         .word @codeF4
         .word @codeF5
         .word @codeF6
         .word @codeF7
         .word @codeF8
         .word @codeF9
         .word @codeFA
         .word @codeFB
         .word @codeFC
         .word @codeFD
         .word @codeFE
         .word @codeFF

         .text
@start:  addu  $t6,$a0,$zero            # t6 = a0
         lw    $t4,0x0000($t6)          # t4 = *t6           pak_code_ptr
         lw    $t1,0x0000($a1)          # t1 = *a1           raw_ptr
         sltu  $v0,$t4,$a2
         beqz  $v0,@error               # if (t4 >= a2) JMP @error
         lui   $v0, @table >> 16
         addiu $t7,$v0, @table & 0xFFFF # t7 = @table offset
         addiu $t2,$t4,0x0001           # t2 = pak + 1       pak_data_ptr
         sltu  $v0,$t1,$a3

@code:   beqz  $v0,@error               # if (t1 >= a3) JMP @error
         nop
         lbu   $a0,0x0000($t4)          # a0 = *t4           get CODE
         nop
         addiu $v1,$a0,0xFFFFFF10       # v1 = a0 + 0xFFFFFF10
         sltiu $v0,$v1,0x0010           # if (v1 >= 0x10) JMP @codeNN
         beqz  $v0,@codeNN              # else JMP @code-X
         sll   $v0,$v1,0x2
         addu  $v0,$v0,$t7
         lw    $v0,0x0000($v0)
         nop
         jr    $v0
         nop

@codeF0: lbu   $t3,0x0000($t2)          # t3 = *t2           get XN (8 bits)
         addiu $t2,$t2,0x0002           # t2 += 2            update pak_data_ptr
         addiu $t4,$t4,0x0002           # t4 += 2            update pak_code_ptr
         andi  $v0,$t3,0x0F             # v0 = t3 & 0x0F     N
         addiu $t0,$v0,0x0003           # t0 = v0 + 3        N += 3
         srl   $t3,$t3,0x04             # t3 >>= 4           X
@loopF0: sb    $t3,0x0000($t1)          # *t1 = t3 <-------+ put X
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         bnez  $t0,@loopF0              # t1++             | update raw_ptr
         addiu $t1,$t1,0x0001           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeF1: lbu   $t3,0x0001($t2)          # t3 = *(t2+1)       get X
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0003           # t2 += 3            update pak_data_ptr
         addiu $t4,$t4,0x0003           # t4 += 3            update pak_code_ptr
         addiu $t0,$v0,0x0004           # t0 = v0 + 4        N += 4
@loopF1: sb    $t3,0x0000($t1)          # *t1 = t3 <-------+ put X
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         bnez  $t0,@loopF1              # t1++             | update raw_ptr
         addiu $t1,$t1,0x0001           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeF2: lbu   $t3,0x0001($t2)          # t3 = *(t2+1)       get YX (8 bits)
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0003           # t2 += 3            update pak_data_ptr
         addiu $t4,$t4,0x0003           # t4 += 3            update pak_code_ptr
         addiu $t0,$v0,0x0002           # t0 = v0 + 2        N += 2
         srl   $a0,$t3,0x04             # a0 = t3 >> 4       Y
         andi  $t3,$t3,0x0F             # t3 &= 0x0F         X
@loopF2: sb    $t3,0x0000($t1)          # *t1 = t3 <-------+ put X
         sb    $a0,0x0001($t1)          # *(t1+1) = a0     | put Y
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         bnez  $t0,@loopF2              # t1 += 2          | update raw_ptr
         addiu $t1,$t1,0x0002           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeF3: lbu   $t3,0x0001($t2)          # t3 = *(t2 + 1)     get X
         lbu   $a0,0x0002($t2)          # a0 = *(t2 + 2)     get Y
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0004           # t2 += 4            update pak_data_ptr
         addiu $t4,$t4,0x0004           # t4 += 4            update pak_code_ptr
         addiu $t0,$v0,0x0002           # t0 = v0 + 2        N += 2
@loopF3: sb    $t3,0x0000($t1)          # *t1 = t3 <-------+ put X
         sb    $a0,0x0001($t1)          # *(t1+1) = a0     | put Y
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         bnez  $t0,@loopF3              # t1 += 2          | update raw_ptr
         addiu $t1,$t1,0x0002           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeF4: lbu   $t3,0x0001($t2)          # t3 = *(t2 + 1)     get X
         lbu   $a0,0x0002($t2)          # a0 = *(t2 + 2)     get Y
         lbu   $t5,0x0003($t2)          # t5 = *(t2 + 3)     get Z
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0005           # t2 += 5            update pak_data_ptr
         addiu $t4,$t4,0x0005           # t4 += 5            update pak_code_ptr
         addiu $v1,$t1,0x0002           # v1 = t1 + 2        stupid ptr
         addiu $t0,$v0,0x0002           # t0 = v0 + 2        N += 2
@loopF4: sb    $t3,0x0000($t1)          # *t1 = t3 <-------+ put X
         sb    $a0,0xFFFFFFFF($v1)      # *(v1 - 1) = a0   | put Y
         sb    $t5,0x0000($v1)          # *v1 = t5         | put Z
         addiu $v1,$v1,0x0003           # v1 += 3          | update stupid ptr
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         bnez  $t0,@loopF4              # t1 += 3          | update raw_ptr
         addiu $t1,$t1,0x0003           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeF5: lbu   $t3,0x0001($t2)          # t3 = *(t2 + 1)     get X
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0003           # t2 += 3            update pak_data_ptr
         addiu $t4,$t4,0x0003           # t4 += 3            update pak_code_ptr
         addiu $t0,$v0,0x0004           # v0 += 4            N += 4
@loopF5: addiu $t2,$t2,0x0001           # t2++ <-----------+ update pak_data_ptr
         sb    $t3,0x0000($t1)          # *t1 = t3         | put X
         lbu   $v0,0x0000($t4)          # v0 = *t4         | get BYTE
         addiu $t4,$t4,0x0001           # t4++             | update pak_code_ptr
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         sb    $v0,0x0001($t1)          # *(t1+1) = v0     | put BYTE
         bnez  $t0,@loopF5              # t1 += 2          | update raw_ptr
         addiu $t1,$t1,0x0002           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeF6: lbu   $t3,0x0001($t2)          # t3 = *(t2 + 1)     get X
         lbu   $a0,0x0002($t2)          # a0 = *(t2 + 2)     get Y
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0004           # t2 += 4            update pak_data_ptr
         addiu $t4,$t4,0x0004           # t4 += 4            update pak_code_ptr
         addiu $v1,$t1,0x0002           # v1 = t1 + 2        stupid ptr
         addiu $t0,$v0,0x0003           # v0 = v0 + 3        N += 3
@loopF6: addiu $t2,$t2,0x0001           # t2++ <-----------+ update pak_data_ptr
         sb    $t3,0x0000($t1)          # *t1 = t3         | put X
         sb    $a0,0xFFFFFFFF($v1)      # *(v1 - 1) = a0   | put Y
         lbu   $v0,0x0000($t4)          # v0 = *t4         | get BYTE
         addiu $t4,$t4,0x0001           # t4++             | update pak_code_ptr
         addiu $t1,$t1,0x0003           # t1 += 3          | update raw_ptr
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         sb    $v0,0x0000($v1)          # *v1 = v0         | put BYTE
         bnez  $t0,@loopF6              # v1 += 3          | update stupid ptr
         addiu $v1,$v1,0x0003           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeF7: lbu   $t3,0x0001($t2)          # t3 = *(t2 + 1)     get X
         lbu   $a0,0x0002($t2)          # a0 = *(t2 + 2)     get Y
         lbu   $t5,0x0003($t2)          # t5 = *(t2 + 3)     get Z
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0005           # t2 += 5            update pak_data_ptr
         addiu $t4,$t4,0x0005           # t4 += 5            update pak_code_ptr
         addiu $v1,$t1,0x0003           # v1 = t1 + 3        stupid ptr
         addiu $t0,$v0,0x0002           # t0 = v0 + 2        N += 2
@loopF7: addiu $t2,$t2,0x0001           # t2++ <-----------+ update pak_data_ptr
         sb    $t3,0x0000($t1)          # *t1 = t3         | put X
         sb    $a0,0xFFFFFFFE($v1)      # *(v1 - 2) = a0   | put Y
         sb    $t5,0xFFFFFFFF($v1)      # *(v1 - 1) = t5   | put Z
         lbu   $v0,0x0000($t4)          # v0 = *t4         | get BYTE
         addiu $t4,$t4,0x0001           # t4++             | update pak_code_ptr
         addiu $t1,$t1,0x0004           # t1 += 4          | update raw_ptr
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         sb    $v0,0x0000($v1)          # *v1 = v0         | put BYTE
         bnez  $t0,@loopF7              # v1 += 4          | update stupid ptr
         addiu $v1,$v1,0x0004           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeF8: lbu   $t3,0x0001($t2)          # t3 = *(t2 + 1)     get X
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0003           # t2 += 3            update pak_data_ptr
         addiu $t4,$t4,0x0003           # t4 += 3            update pak_code_ptr
         addiu $t0,$v0,0x0004           # t0 = v0 + 4        N += 4
@loopF8: sb    $t3,0x0000($t1)          # *t1 = t3 <-------+ put X
         addiu $t1,$t1,0x0001           # t1++             | update raw_ptr
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         bnez  $t0,@loopF8              # t3++             | X++
         addiu $t3,$t3,0x0001           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeF9: lbu   $t3,0x0001($t2)          # t3 = *(t2 + 1)     get X
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0003           # t2 += 3            update pak_data_ptr
         addiu $t4,$t4,0x0003           # t4 += 3            update pak_code_ptr
         addiu $t0,$v0,0x0004           # t0 = v0 + 4        N += 4
@loopF9: sb    $t3,0x0000($t1)          # *t1 = t3 <-------+ put X
         addiu $t1,$t1,0x0001           # t1++             | update raw_ptr
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         bnez  $t0,@loopF9              # t3--             | X--
         addiu $t3,$t3,0xFFFFFFFF       # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeFA: lbu   $t3,0x0001($t2)          # t3 = *(t2 + 1)     get X
         lbu   $a0,0x0002($t2)          # a0 = *(t2 + 2)     get I
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         addiu $t2,$t2,0x0004           # t2 += 4            update pak_data_ptr
         addiu $t4,$t4,0x0004           # t4 += 4            update pak_code_ptr
         addiu $t0,$v0,0x0005           # t0 = v0 + 5        N += 5
@loopFA: sb    $t3,0x0000($t1)          # *t1 = t3 <-------+ put X
         addiu $t1,$t1,0x0001           # t1++             | update raw_ptr
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         bnez  $t0,@loopFA              # t3 += a0         | X += I
         addu  $t3,$t3,$a0              # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeFB: lbu   $t3,0x0001($t2)          # t3 = *(t2 + 1)     get X
         lbu   $a0,0x0002($t2)          # a0 = *(t2 + 2)     get Y
         lbu   $v0,0x0000($t2)          # v0 = *t2           get N
         lbu   $v1,0x0003($t2)          # v1 = *(t2 + 3)     get I
         addiu $t2,$t2,0x0005           # t2 += 5            update pak_data_ptr
         addiu $t4,$t4,0x0005           # t4 += 5            update pak_code_ptr
         addiu $t0,$v0,0x0003           # t0 = v0 + 3        N += 3
         sll   $t5,$v1,0x18             # t5 = v1 << 24      I<<24
@loopFB: sb    $t3,0x0000($t1)          # *t1 = t3 <-------+ put X
         sb    $a0,0x0001($t1)          # *(t1 + 1) = a0   | put Y
         addiu $t1,$t1,0x0002           # t1 += 2          | update raw_ptr
         sra   $v0,$t5,0x18             # v0 = t5 >> 24 (*)| I (sign extended)
         sll   $a0,$a0,0x08             # a0 <<= 8         | Y*256
         andi  $v1,$t3,0xFF             # v1 = t3 & 0xFF   | X
         or    $v1,$v1,$a0              # v1 |= a0         | YX (16 bits)
         addu  $v0,$v0,$v1              # v0 += v1         | YX + I (16 bits)
         addu  $t3,$v0,$zero            # t3 = v0          | X
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         bnez  $t0,@loopFB              # a0 = v0 >> 8     | Y
         srl   $a0,$v0,0x08             # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeFC: lbu   $t3,0x0000($t2)          # t3 = *t2           get XY (8 bits)
         lbu   $a0,0x0001($t2)          # a0 = *(t2 + 1)     get NZ (8 bits)
         addiu $t2,$t2,0x0003           # t2 += 3            update pak_data_ptr
         addiu $t4,$t4,0x0003           # t4 += 3            update pak_code_ptr
         srl   $v0,$a0,0x04             # v0 = a0 >> 4       N
         addiu $t0,$v0,0x0004           # t0 = v0 + 4        N += 4
         andi  $v0,$a0,0x0F             # v0 = a0 & 0x0F     Z
         sll   $v0,$v0,0x08             # v0 <<= 8           Z*256
         or    $v0,$t3,$v0              # v0 |= t3           ZXY (16 bits)
         subu  $v1,$t1,$v0              # v1 = t1 - v0       offset ($-ZXY)
@loopFC: lbu   $v0,0xFFFFFFFF($v1)      # v0 = *(v1 - 1) <-+ get BYTE
         addiu $v1,$v1,0x0001           # v1++             | offset++
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         sb    $v0,0x0000($t1)          # *t1 = v0         | put BYTE
         bnez  $t0,@loopFC              # t1++             | update raw_ptr
         addiu $t1,$t1,0x0001           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeFD: lbu   $t3,0x0000($t2)          # t3 = *t2           get X
         lbu   $v0,0x0001($t2)          # a0 = *(t2 + 1)     get N
         addiu $t2,$t2,0x0003           # t2 += 3            update pak_data_ptr
         addiu $t4,$t4,0x0003           # t4 += 3            update pak_code_ptr
         addiu $t0,$v0,0x0014           # t0 = v0 + 0x14     N += 20
         subu  $v1,$t1,$t3              # v1 = t1 - t3       offset ($-X)
@loopFD: lbu   $v0,0xFFFFFFFF($v1)      # v0 = *(v1 - 1) <-+ get BYTE
         addiu $v1,$v1,0x0001           # v1++             | offset++
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         sb    $v0,0x0000($t1)          # *t1 = v0         | put BYTE
         bnez  $t0,@loopFD              # t1++             | update raw_ptr
         addiu $t1,$t1,0x0001           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeFE: lbu   $t3,0x0000($t2)          # t3 = *t2           get XN (8 bits)
         addiu $t2,$t2,0x0002           # t2 += 2            update pak_data_ptr
         addiu $t4,$t4,0x0002           # t4 += 2            update pak_code_ptr
         andi  $v0,$t3,0x0F             # v0 = t3 & 0x0F     N
         addiu $t0,$v0,0x0003           # t0 = v0 + 3        N += 3
         andi  $v0,$t3,0xF0             # v0 = t3 & 0xF0;    X*16
         srl   $v0,$v0,0x01             # v0 >>= 1           X*8
         subu  $v1,$t1,$v0              # v1 = t1 - v0       offset ($-X*8)
@loopFE: lbu   $v0,0xfffffff8($v1)      # v0 = *(v1 - 8) <-+ get BYTE
         addiu $v1,$v1,0x0001           # v1++             | offset++
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         sb    $v0,0x0000($t1)          # *t1 = v0         | put BYTE
         bnez  $t0,@loopFE              # t1++             | update raw_ptr
         addiu $t1,$t1,0x0001           # if (t0) JMP >----+ loop while N
         j     @next                    # *t6 = t4           update pak_ptr
         sw    $t4,0x0000($t6)          # JMP @next          another code

@codeFF: addu  $v0,$zero,$zero          # v0 = 0             EXIT_OK
         sw    $t2,0x0000($t6)          # *t6 = t2           save pak_data_ptr
         jr    $ra                      # *a1 = t1           save raw_ptr
         sw    $t1,0x0000($a1)          # exit               end

@codeNN: addiu $t2,$t2,0x0001           # t2++               update pak_data_ptr
         addiu $t4,$t4,0x0001           # t4++               update pak_code_ptr
         addiu $t0,$a0,0x0001           # t0 = a0 + 1        N + 1
@loopNN: addiu $t2,$t2,0x0001           # t2++ <-----------+ update pak_data_ptr
         lbu   $v0,0x0000($t4)          # v0 = *t4         | get X
         addiu $t4,$t4,0x0001           # t4++             | update pak_code_ptr
         addiu $t0,$t0,0xFFFFFFFF       # t0--             | N--
         sb    $v0,0x0000($t1)          # *t1 = v0         | put X
         bnez  $t0,@loopNN              # t1++             | update raw_ptr
         addiu $t1,$t1,0x0001           # if (t0) JMP >----+ loop while N
         sw    $t4,0x0000($t6)          # *t6 = t4           update pak_ptr

@next:   sltu  $v0,$t4,$a2              # if (t4 < a2) JMP @code
         bnez  $v0,@code
         sltu  $v0,$t1,$a3

@error:  sw    $t1,0x0000($a1)          # *a1 = t1           save raw_ptr
         jr    $ra                      # v0 = 1             EXIT_FAIL
         li    $v0,0x0001               # exit               end

# EOF
